#include "snk_adaptor.h"
#include "cashdevice_def.h"
#include "foundation/finance/common/plugin_mgr/include/plugin.h"
#include <string>
#include <map>
#include <string.h>
#include <memory>

using namespace SwanLink::Finance;

#define PARAM_CHECK(cond, status) do {\
    if ((cond)) {\
        m_iStatus = SUCCESS;\
    } else {\
        m_iStatus = status;\
        return m_iStatus;\
    } \
}while(0)

#define CMD_PRINT(p, len) do {\
    for (int i = 0; i < (len); i++) {\
        printf("%02X ", (p)[i]);\
    }\
    printf("\n");\
}while(0)

std::map<int, std::string> g_sErr = {
      {0xff,   "未知错误"},
      {0x03,   "指令格式错误"},
      {0x09 ,   "没有授权"},
      {0x0a ,   "没有版本号"},
      {0x0e ,   "密钥不存在"},
      {0x0f ,   "密钥已存在"},
      {0x10 ,   "密钥属性错"},
      {0x11,    "口令不成功"},
      {0x12,    "密钥没有装载成功"},
      {0x13,    "权限已过期"},
      {0x15,    "授权已过期"},
      {0x17,    "密钥号超出范围"},
      {0x18,    "填充数据错误"},
      {0x19,    "无PIN"},
      {0x1a,    "PIN已失效"},
      {0x1b,    "相邻两次取PIN时间太短"},
      {0x1c,    "PIN长度错"},
      {0x1d,    "卡号长度错"},
      {0x20,    "内存耗尽"},
      {0x24,    "键盘被锁定"},
      {0x25,    "填充数据长度错"},
      {0x26,    "在规定时间内没有完成操作"},
      {0x30,  "PINBLOCK运算时卡号或填充数据格式错误"},
      {0x35,    "EPP没有初始化"}
};

const char *XZ_F31_strerror(int code) {
    if (g_sErr.find(code) == g_sErr.end()) {
        return g_sErr[0xff].c_str();
    }
    return g_sErr[code].c_str();
}

SNK008A_Adaptor::SNK008A_Adaptor()
{
    m_iFd = 0;
    m_iStatus = ERR_DEVICE_NOT_OPEN;
    m_timeout = 3000;
}

SNK008A_Adaptor::~SNK008A_Adaptor()
{
}

int SNK008A_Adaptor::Open(const Transport &tran)
{
    m_iFd = SUNSON_OpenCom("USB", 0);
    PARAM_CHECK(m_iFd > 0, ERROR_KEYBOARD_OPEN_FAIL);
    SUNSON_SetEPPSMMode(0x01);
    unsigned char ret_info[64] = {0};
    SUNSON_EnableSystemKeyboard(0, ret_info);
    return m_iStatus;
}
// m_Fd != 0 则是打开状态
bool SNK008A_Adaptor::IsOpen()
{
    if (m_iFd == 0) {
       return false;
    }
    unsigned char SerialNumber[128] = {0};
    int ret = SUNSON_ReadSN(SerialNumber);
    if (ret > 0) {
       return true;
    }
    return false;
}

int SNK008A_Adaptor::Close()
{
    PARAM_CHECK(m_iFd > 0, ERROR_KEYBOARD_OPEN_FAIL);
    int result = SUNSON_CloseCom();
    unsigned char ret_info[64] = {0};
    PARAM_CHECK(result > 0, ERROR_KEYBOARD_DEVICE_NOT_OPEN);
    SUNSON_EnableSystemKeyboard(1, ret_info);
    m_iFd = 0;
    return m_iStatus;
}

int SNK008A_Adaptor::Reset()
{
    PARAM_CHECK(m_iFd > 0, ERROR_KEYBOARD_DEVICE_NOT_OPEN);

    unsigned char ReturnInfo[64] = {0};
    int result =  SUNSON_ResetEpp(ReturnInfo);
    CMD_PRINT(ReturnInfo, result);
    PARAM_CHECK(result > 0, ERR_KEYBOARD_HW_ERROR);
    return m_iStatus;
}

int SNK008A_Adaptor::GetStatus(int module, char *err)
{
    uint64_t status = 0;
    int ret = 0;
    ret = SUNSON_GetEppStatus((unsigned char*)&status);
    printf("status = %lu", status);
    PARAM_CHECK(ret > 0, ERR_DEVICE_NOT_OPEN);
    return m_iStatus;
}

bool SNK008A_Adaptor::IsSM4Enabled() {
    return true;
}

int SNK008A_Adaptor::GetSN(char sn[], int &len)
{
    PARAM_CHECK(m_iFd != 0, ERR_DEVICE_NOT_OPEN);

    int ret = SUNSON_ReadSN((unsigned char*)sn);
    PARAM_CHECK(ret > 0, ERR_GET_SN);
    return m_iStatus;
}

int SNK008A_Adaptor::CalculateMAC(const KeyInfo& key, char plain[], int len, char enc_data[], int &enc_len)
{
    PARAM_CHECK(m_iFd != 0, ERR_DEVICE_NOT_OPEN);
    PARAM_CHECK(key.keyID <= 11 && key.keyID  >= 0, ERR_KEY_ID_OUT_OF_RANGE);
    PARAM_CHECK(key.keyType == 280 || key.keyType == 281 || key.keyType == 285, ERR_KEY_ID_OUT_OF_RANGE);
    if (key.user_key_type != 261) {
        return ERR_KEY_USER_TYPE_OUT_OF_RANGE;
    }
    int length = strlen(plain);
    PARAM_CHECK(length > 0, ERR_KEY_ID_OUT_OF_RANGE);

    enc_len = 0;
    unsigned char nEncryDataLen = 8;
    if (key.keyType == KEY_TYPE_SM4) {
        int ret = SUNSON_MakeMac(key.keyID, 0x30, nEncryDataLen, (unsigned char*)plain, (unsigned char*)enc_data);
        PARAM_CHECK(ret > 0, ERR_GET_MAC);
        enc_len = 8;
    } else if (key.keyType == KEY_TYPE_RSA) {
        return ERROR_KEYBOARD_DEVICE_NOT_SUPPORTED;
    } else if (key.keyType == KEY_TYPE_DES) {
        int ret = SUNSON_MakeMac(key.keyID, 0x30, nEncryDataLen, (unsigned char*)plain, (unsigned char*)enc_data);
        PARAM_CHECK(ret > 0, ERR_GET_MAC);
        enc_len = 8;
    }
    CMD_PRINT(enc_data, enc_len);
    PARAM_CHECK(enc_len > 0, ERR_GET_MAC);
    return m_iStatus;
}

int SNK008A_Adaptor::EncryptData(const KeyInfo& key, char plain[], int len, char enc_data[], int &len_enc)
{
    PARAM_CHECK(m_iFd != 0, ERR_DEVICE_NOT_OPEN);
    PARAM_CHECK(key.keyID <= 11 && key.keyID  >= 0, ERR_KEY_ID_OUT_OF_RANGE);
    PARAM_CHECK(key.keyType == 280 || key.keyType == 281 || key.keyType == 285, ERR_KEY_ID_OUT_OF_RANGE);
    if (key.user_key_type != 262) {
        return ERR_KEY_USER_TYPE_OUT_OF_RANGE;
    }
    int length = strlen(plain);
    PARAM_CHECK(length > 0, ERR_KEY_ID_OUT_OF_RANGE);

    if (key.keyType == KEY_TYPE_SM4) {
        len_enc = SUNSON_DataCompute(key.keyID, 0x31, 0x30, 0x00, length,
                  (unsigned char*)plain, (unsigned char*)enc_data);
    } else if (key.keyType == KEY_TYPE_DES || key.keyType == KEY_TYPE_RSA) {
        len_enc = SUNSON_DataCompute(key.keyID, 0x31, 0x32, 0x00, length,
                  (unsigned char*)plain, (unsigned char*)enc_data);
    }
    CMD_PRINT(enc_data, len_enc);
    PARAM_CHECK(len_enc > 0, ERR_GET_DATA_FAIL);
    return m_iStatus;
}

int SNK008A_Adaptor::SetPINLength(int minLen, int maxLen)
{
    PARAM_CHECK(m_iFd != 0, ERR_DEVICE_NOT_OPEN);
    PARAM_CHECK(minLen <= maxLen && minLen >=1 && maxLen == 6, ERR_DATA_OUT_OF_RANGE);
    return m_iStatus;
}

int SNK008A_Adaptor::SetTextMode(TextMode mode)
{
    PARAM_CHECK(m_iFd != 0, ERR_DEVICE_NOT_OPEN);
    int ret = 0;
    m_mode = mode;
     if (m_mode == TEXT_MODE_PLAIN) {
        ret = SUNSON_EnableUSBplaintext(0x01);
    } else {
        ret = SUNSON_EnableUSBplaintext(0x02);
    }
    PARAM_CHECK(ret > 0, ERR_DEVICE_NOT_OPEN);
    return m_iStatus;
}

void SNK008A_Adaptor::ClearTextMode()
{
    OnCharProcess(KEY_CLEAR, 0);
}

int SNK008A_Adaptor::LoadMasterKey(KeyID masterKeyID, unsigned char *KeyValue)
{
    PARAM_CHECK(m_iFd != 0, ERR_DEVICE_NOT_OPEN);
    PARAM_CHECK(masterKeyID <= 15 && masterKeyID  >= 0, ERR_KEY_ID_OUT_OF_RANGE);
    int len = strlen((const char*)KeyValue);
    PARAM_CHECK(len == 8 || len == 16, ERR_DATA_OUT_OF_RANGE);

    unsigned char ReturnInfo = 0;
    int ret = SUNSON_LoadUserKey(masterKeyID, 65535, 0x01, len, KeyValue, &ReturnInfo);
    CMD_PRINT(&ReturnInfo, ret);
    printf("%s\n", XZ_F31_strerror(ReturnInfo));
    PARAM_CHECK(ReturnInfo > 0, ERR_KEYBOARD_HW_ERROR);
    return m_iStatus;
}

int SNK008A_Adaptor::LoadUserKey(KeyID masterKeyID, userKeyType user_Key_Type, unsigned char *KeyValue)
{
    PARAM_CHECK(m_iFd != 0, ERR_DEVICE_NOT_OPEN);
    PARAM_CHECK(masterKeyID <= 15 && masterKeyID  >= 0, ERR_KEY_ID_OUT_OF_RANGE);
    PARAM_CHECK(user_Key_Type >= 260 && user_Key_Type <= 262, ERR_KEY_ID_OUT_OF_RANGE);
    if ((const char*)KeyValue == nullptr) {
        return ERR_KEY_VAULE_NULL;
    }
    int len = strlen((const char*)KeyValue);
    PARAM_CHECK(len == 8 || len == 16, ERR_DATA_OUT_OF_RANGE);

    unsigned char ReturnInfo = 0;
    int attr = 0;
    if (user_Key_Type == USER_KEY_ID_PIN) {
        attr = 0x00000002;
    } else if (user_Key_Type == USER_KEY_ID_MAC) {
        attr = 0x00000004;
    } else if (user_Key_Type == USER_KEY_ID_DATA) {
        attr = 0x00000001;
    }
    int ret = SUNSON_LoadUserKey(masterKeyID, 65535, attr, len, KeyValue, &ReturnInfo);
    CMD_PRINT(&ReturnInfo, ret);
    printf("%s\n", XZ_F31_strerror(ReturnInfo));
    PARAM_CHECK(ReturnInfo > 0, ERR_KEYBOARD_HW_ERROR);
    return m_iStatus;
}

int SNK008A_Adaptor::UpdateMasterKey(KeyID masterKeyID, unsigned char *KeyValue)
{
    return LoadMasterKey(masterKeyID, KeyValue);
}

int SNK008A_Adaptor::UpdateUserKey(KeyID masterKeyID, userKeyType user_Key_Type, unsigned char *KeyValue)
{
    return LoadUserKey(masterKeyID, user_Key_Type, KeyValue);
}

int SNK008A_Adaptor::DeleteUserKey(KeyID masterKeyID, userKeyType user_Key_Type)
{
    unsigned char ReturnInfo = 0;
    int ret = SUNSON_DelSpecialUserKey(masterKeyID, &ReturnInfo);
    CMD_PRINT(&ReturnInfo, 1);
    PARAM_CHECK(ret > 0, ERR_KEYBOARD_HW_ERROR);
    return m_iStatus;
}

void SNK008A_Adaptor::OnCharProcess(int code, char ch)
{
    switch (code) {
    case KEY_CHAR:
    case KEY_SHARP:
       if (keybuf_len < 6) {
            keybuf[keybuf_len++] = ch;
            OnResult(keybuf, keybuf_len);
             break;
       } else {
         OnFailed(ERR_KEYBOARD_OUT_OF_RANGE, "密码长度超出范围");
         break;
       }
    case KEY_OK:
        if (keybuf_len < 6) {
            OnFailed(ERR_KEYBOARD_LENGTH_TOO_SHORT, "密码长度过短");
            break;
        } else if (keybuf_len > 6) {
            OnFailed(ERR_KEYBOARD_LENGTH_MECISM, "密码长度过长");
            break;
        } else {
            OnResult(keybuf, keybuf_len);
            break;
        }
    case KEY_BACKSPACE:
        if (keybuf_len <= 0) {
            OnFailed(ERR_KEYBOARD_HW_ERROR, "长度为0,无法退格");
            break;
        }
        keybuf[--keybuf_len] = '\0';
        OnResult(keybuf, keybuf_len);
        break;
    case KEY_RETURN:
    case KEY_CLEAR:
        keybuf_len = 0;
        keybuf[0] = '\0';
        OnResult(keybuf, keybuf_len);
        break;
    case KEY_UP:
    case KEY_DOWN:
    case KEY_DROP:
         OnFailed(ERR_KEYBOARD_INVALID_KEY, "无效字符");
         break;
    case KEY_EXIT:
        break;
    }
}

void SNK008A_Adaptor::OnFailed(int code, const std::string &err)
{
    if (pinListener) {
        pinListener->OnFailed(code, err);
    }
}

void SNK008A_Adaptor::OnResult(char *buf, int len)
{
    if (pinListener) {
        pinListener->OnResult(buf, len);
    }
}

void SNK008A_Adaptor::ListenForRead()
{
    flag = true;
    keybuf_len = 0;

    unsigned char ReturnInfo = 0;
    if (m_timeout > 30000) {
        OnFailed(ERR_KEYBOARD_TIME_OUT_OF_RANGE, "键盘监听值超出范围");
        flag = false;
        return;
    }
    int ret = SUNSON_GetPin(6, 6, 0x01, &ReturnInfo);
    if (ret == 0) {
        OnFailed(ReturnInfo, XZ_F31_strerror(ReturnInfo));
        return;
    }
    clock_t start1, finish1, internalTime1;
    unsigned char timeinfo = 0;
    int result =  SUNSON_SetLockKeyTimeOut(m_timeout, &timeinfo);
    internalTime1 = m_timeout*1000;
    start1 = clock();
    while (flag) {
        finish1 = clock();
        if ((finish1 - start1) > internalTime1 || result == 0) {
            OnFailed(ERR_KEYBOARD_INPUT_TIME_OUT, "键盘输入超时");
            flag = false;
            break;
        }
        unsigned char ch = 0;

        int ret  = SUNSON_ScanKeyPress(&ch);
        if (ret == 0) {
            continue;
        }
        CMD_PRINT(&ch, 1);
        if (ch >= '0' && ch <= '9') {
            if (m_mode == 1) {
              OnCharProcess(KEY_CHAR, ch);
            } else {
               OnCharProcess(KEY_CHAR, ch);
            }
        } else if (ch == 0x2e) {
            OnCharProcess(KEY_DROP, ch);
        } else if (ch == 0x1b) {
            OnCharProcess(KEY_RETURN, ch);
            flag = false;
        } else if (ch == 0x0d || ch == 0xaa) {
            OnCharProcess(KEY_OK, ch);
            flag = false;
        } else if (ch == 0x08) {
            OnCharProcess(KEY_BACKSPACE, ch);
        } else if (ch == 0x21) {
            OnCharProcess(KEY_UP, ch);
            flag = false;
        } else if (ch == 0x22) {
            OnCharProcess(KEY_DOWN, ch);
            flag = false;
        } else {
            OnCharProcess(KEY_CHAR, ch);
        }

        start1 = clock();
    }

    flag = false;
}

void SNK008A_Adaptor::ListenForPin(PinListener* listener, int timeout)
{
    do {
        std::lock_guard<std::mutex> lock(pin_mutex);
        flag = false;
        m_timeout = timeout;
        pinListener = listener;
    }while(false);
    pthread_create(&pin_thread_id, NULL, ListenForPinThread, this);
    pthread_join(pin_thread_id, NULL);
    pinListener = nullptr;
}
// 插件接口实现
std::shared_ptr<IAdaptor> CreateAdaptor()
{
    return std::make_shared<SNK008A_Adaptor>();
}

Transport GetAccess(ACCESS_MODE mode, const char *dev)
{
    SerialTransport tran;
    tran.mode = mode;
    strcpy(tran.dev, dev);
    tran.timeout = 3000;
    tran.baudrate = 115200;
    return tran;
}

static Plugin g_plugin = {
    .CreateAdaptor = CreateAdaptor,
    .GetAccess = GetAccess,
    .version = "1.0",
    .name = "SNK008A",
    // 至少包含设备型号
    .desc = "SNK008A Printer Adaptor 1.0",
    .id = 0x0302
};

PLUGIN_INIT(&g_plugin)